package com.parsechina.hpush.broker.spi.persistence.h2;

import com.parsechina.hpush.broker.config.HPushConfig;
import com.parsechina.hpush.broker.exception.MqttException;
import com.parsechina.hpush.broker.internal.subscriptions.SubscriptionsStore;
import com.parsechina.hpush.broker.spi.*;
import org.h2.mvstore.MVStore;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.File;
import java.io.IOException;
import java.util.concurrent.Executors;
import java.util.concurrent.ScheduledExecutorService;
import java.util.concurrent.TimeUnit;

/**
 *
 * MapDB main persistence implementation
 * @author linfeng
 */
public class H2PersistentStore implements IPersistentStore {

    private static final Logger LOG = LoggerFactory.getLogger(H2PersistentStore.class);

    private MVStore mvStore;

    private final ScheduledExecutorService m_scheduler = Executors.newScheduledThreadPool(1);
    private H2MessagesStore messagesStore;
    private H2SessionsStore sessionsStore;
    private H2ClientStore clientStore;

    public H2PersistentStore(HPushConfig props) {

        String storePath = props.getPersistentStorePath();
        // in seconds
        int autosaveInterval = props.getAutoSaveInterval();
        boolean isStoreMemory = props.getPersistentMemory();

        if (isStoreMemory || storePath == null || storePath.isEmpty()) {
            mvStore = new MVStore.Builder()
                    .autoCommitDisabled()
                    .open();
        } else {
            File tmpFile;
            try {
                tmpFile = new File(storePath);
                boolean fileNewlyCreated = tmpFile.createNewFile();
                LOG.info("Starting with {} [{}] db file", fileNewlyCreated ? "fresh" : "existing", storePath);
            } catch (IOException ex) {
                LOG.error(null, ex);
                throw new MqttException("Can't create temp file for subscriptions storage [" + storePath + "]", ex);
            }
            mvStore = new MVStore.Builder()
                    .fileName(storePath)
                    .autoCommitDisabled()
                    .open();
        }
        m_scheduler.scheduleWithFixedDelay(() -> mvStore.commit(), autosaveInterval, autosaveInterval, TimeUnit.SECONDS);

        //TODO check mDb is valid and
        messagesStore = new H2MessagesStore(mvStore);
        messagesStore.initStore();

        sessionsStore = new H2SessionsStore(mvStore, messagesStore);
        sessionsStore.initStore();

        clientStore = new H2ClientStore(mvStore);
        clientStore.initStore();

    }

    /**
     * Factory method to create message store backed by MapDB
     */
    @Override
    public IMessagesStore messagesStore() {
        return messagesStore;
    }

    @Override
    public ISessionsStore sessionsStore() {
        return sessionsStore;
    }

    @Override
    public IAuthenticator authenticatorStore() {
        return null;
    }

    @Override
    public SubscriptionsStore subscriptionsStore() {
        return new SubscriptionsStore(sessionsStore());
    }

    @Override
    public IClientStore clientStore() {
        return clientStore;
    }

    @Override
    public void close() {
        if (mvStore.isClosed()) {
            LOG.debug("already closed");
            return;
        }
        mvStore.commit();
        mvStore.close();
        LOG.debug("closed disk storage");
        m_scheduler.shutdown();
        LOG.debug("Persistence commit scheduler is shutdown");
    }

}
